Skip to main content

Introspection trong Java

Java Reflection không chỉ cho phép bạn "phản chiếu" (reflect) cấu trúc của các lớp mà còn hỗ trợ Introspection – khả năng truy cập và thay đổi các thành viên (members) của lớp, bao gồm các field, method và constructor, ngay cả khi chúng được khai báo với phạm vi truy cập private. Phần dưới đây sẽ bổ sung nội dung về introspection, tập trung vào việc truy cập và sửa đổi các thành viên của đối tượng.


8. Introspection – Truy Cập và Thay Đổi Members

Introspection là quá trình cho phép chương trình tự kiểm tra cấu trúc của một đối tượng trong thời gian chạy (runtime). Nhờ có Reflection, bạn có thể:

  • Truy cập các field: Lấy danh sách các trường, đọc và thay đổi giá trị của chúng.
  • Truy cập các method: Lấy danh sách các phương thức, gọi chúng ngay cả khi chúng không phải là public.
  • Truy cập constructor: Lấy các constructor và sử dụng chúng để khởi tạo đối tượng.

Dưới đây là các ví dụ minh họa cụ thể:

8.1. Truy Cập và Thay Đổi Field

Giả sử bạn có lớp Student:

public class Student {
private String name;
private int age;

public Student() {
this.name = "Unknown";
this.age = 0;
}

public Student(String name, int age) {
this.name = name;
this.age = age;
}

public String getName() {
return name;
}

public int getAge() {
return age;
}
}

a. Lấy Danh Sách Field và In Ra Tên của Chúng

Class<Student> studentClass = Student.class;
Field[] fields = studentClass.getDeclaredFields();
for (Field field : fields) {
System.out.println("Field: " + field.getName());
}

b. Đọc và Sửa Giá Trị Của Field Private

try {
// Tạo đối tượng Student
Student student = new Student("John", 20);

// Lấy field private "name"
Field nameField = studentClass.getDeclaredField("name");
nameField.setAccessible(true); // Bỏ qua kiểm tra truy cập private

// Đọc giá trị của field "name"
String nameValue = (String) nameField.get(student);
System.out.println("Tên ban đầu: " + nameValue);

// Thay đổi giá trị của field "name"
nameField.set(student, "Jane");
System.out.println("Tên sau khi thay đổi: " + student.getName());

// Tương tự, với field "age"
Field ageField = studentClass.getDeclaredField("age");
ageField.setAccessible(true);

int ageValue = ageField.getInt(student);
System.out.println("Tuổi ban đầu: " + ageValue);

ageField.setInt(student, 25);
System.out.println("Tuổi sau khi thay đổi: " + student.getAge());

} catch (NoSuchFieldException | IllegalAccessException e) {
e.printStackTrace();
}

8.2. Truy Cập và Gọi Phương Thức

Giả sử bạn có lớp MathOperations với các phương thức public và private:

public class MathOperations {
public int add(int a, int b) {
return a + b;
}

private int subtract(int a, int b) {
return a - b;
}
}

a. Gọi Public Method

try {
MathOperations mathOps = new MathOperations();
Class<?> mathClass = mathOps.getClass();

// Lấy public method "add"
Method addMethod = mathClass.getMethod("add", int.class, int.class);
int sum = (int) addMethod.invoke(mathOps, 10, 5);
System.out.println("10 + 5 = " + sum);

} catch (Exception e) {
e.printStackTrace();
}

b. Gọi Private Method

try {
MathOperations mathOps = new MathOperations();
Class<?> mathClass = mathOps.getClass();

// Lấy private method "subtract"
Method subtractMethod = mathClass.getDeclaredMethod("subtract", int.class, int.class);
subtractMethod.setAccessible(true);
int diff = (int) subtractMethod.invoke(mathOps, 10, 5);
System.out.println("10 - 5 = " + diff);

} catch (Exception e) {
e.printStackTrace();
}

8.3. Khởi Tạo Đối Tượng Qua Constructor (Introspection Constructor)

Như đã trình bày ở phần trước, bạn có thể lấy các constructor để tạo đối tượng, kể cả khi chúng không public.

try {
Class<Student> clazz = Student.class;

// Lấy constructor có 2 tham số
Constructor<Student> constructor = clazz.getDeclaredConstructor(String.class, int.class);
Student student = constructor.newInstance("Michael", 22);
System.out.println("Student: " + student.getName() + ", " + student.getAge());

} catch (Exception e) {
e.printStackTrace();
}

9. Kết Luận

Introspection thông qua Reflection cho phép bạn:

  • Truy cập danh sách các thành viên của một lớp, bao gồm cả field, method và constructor.
  • Đọc và sửa đổi giá trị của field, kể cả những field được khai báo với phạm vi truy cập private.
  • Gọi các phương thức (public và private) dựa trên tên và kiểu tham số, từ đó kích hoạt logic của đối tượng một cách động.
  • Khởi tạo đối tượng bằng cách lấy và gọi constructor theo cách linh hoạt.

Những khả năng này giúp tạo ra các ứng dụng, framework hay công cụ kiểm thử mạnh mẽ, nơi mà cấu trúc của đối tượng cần được xử lý một cách linh hoạt, tổng quát mà không phụ thuộc vào cấu trúc cụ thể của lớp tại thời điểm biên dịch. Tuy nhiên, do tính chất động và khả năng phá vỡ tính đóng gói, việc sử dụng introspection cần được cân nhắc cẩn thận để tránh ảnh hưởng tiêu cực đến hiệu năng và tính bảo mật của ứng dụng.

Qua các ví dụ trên, bạn có thể thấy được cách sử dụng Reflection để thực hiện introspection, từ việc truy cập, in ra danh sách thành viên, cho tới việc thay đổi giá trị của các field và gọi các phương thức một cách linh hoạt. Đây chính là sức mạnh của Reflection trong Java, mở ra nhiều khả năng cho lập trình viên trong việc xây dựng các giải pháp động và mở rộng.